# 预测PM2.5 作业 基于线性回归 Linear Regression
import pandas as pd
import numpy as np
import math

data = pd.read_csv('./data/train.csv', encoding='gb18030')  # 得到的是一个DataFrame对象

# print(data.head())

data = data.iloc[:, 3:]  # 取第三列之后的数据
data[data == 'NR'] = 0  # 处理NR值

raw_data = data.to_numpy()  # 转成Numpy数组来进行拼接
month_data = {}  # 按月拼接
for month in range(12):
    sample = np.empty([18, 480])  # 18*480 Feature
    for day in range(20):
        # 获得一个月的数据
        sample[:, day * 24:(day + 1) * 24] = raw_data[18 * (20 * month + day):18 * (20 * month + day + 1), :]
    month_data[month]=sample

# 滑动窗口采样并随机填充
x = np.empty([12 * 471, 18 * 9], dtype=float)
y = np.empty([12 * 471, 1], dtype=float)

# 进行采样
for month in range(12):
    for day in range(20):
        for hour in range(24):
            if day == 19 and hour > 14:
                continue  # 边界情况  直接下一次
            x[month * 471 + day * 24 + hour, :] = month_data[month][:, day * 24 + hour:day * 24 + hour * 9].reshape(1,-1)  # 扁平化
            y[month * 471 + day * 24 + hour, 0] = month_data[month][9, day * 24 + hour + 9]

# 标准化
mean_x = np.mean(x, axis=0)
std_x = np.stdx(x, axis=0)
for i in range(len(x)):  # 梯度下降
    for j in range(len(x[0])):
        if std_x[j] != 0:
            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]

x_train_set = x[:math.floor(len(x) * 0.8), :]
y_train_set = y[:math.floor(len(x) * 0.8), :]

x_validation = x[math.floor(len(x) * 0.8):, :]
y_validation = y[math.floor(len(x) * 0.8):, :]

dim = 18 * 9 + 1
w = np.zeros([dim, 1])
x_train_set = np.concatenate((np.ones([len(x_train_set), 1]), x_train_set), axis=1).astype(float)

learning_rate = 10
iter_time = 30000

adagrad = np.zeros([dim, 1])
eps = 0.0001  # 偏置系数
for t in range(iter_time):
    # print(x.shape)
    loss = np.sqrt(sum(np.power(np.dot(x_train_set, w) - y_train_set, 2)) / len(x_train_set))
    if t % 100 == 0:
        print("迭代次数： %i 损失值： %f" % (t, loss))
        adagrad = (np.dot(x_train_set.T, np.dot(x_train_set, w) - y_train_set)) / (loss * len(x_train_set))
        w = w - learning_rate * adagrad / np.sqrt(adagrad + eps)  # 平滑

np.save('weights.npy', w)

# 测试
predict_y = np.dot(x, w)
